﻿using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Dsl.Ped.Common;
using Dsl.Ped.Common.Helpers;
using Dsl.Ped.Desktop.Commands;
using Dsl.Ped.Desktop.Helpers;
using Dsl.Ped.Desktop.Messages;
using Dsl.Ped.Desktop.Modules;
using Dsl.Ped.Desktop.Views;
using Dsl.Ped.Desktop.Windows;
using Dsl.Ped.Framework.AddIns;
using GalaSoft.MvvmLight.Command;

namespace Dsl.Ped.Desktop.ViewModels
{
    /// <summary>
    /// View model of <see cref="RibbonBarView"/>
    /// </summary>
    public class RibbonBarViewModel : ViewModelBase
    {
        #region Fields

        /// <summary>
        /// Command executed when the "about" option is clicked
        /// </summary>
        private RelayCommand aboutCommand = null;

        #endregion

        #region SubClasses

        /// <summary>
        /// Abstract base class of placeholders that takes their place in a RibbonBar
        /// </summary>
        public abstract class Placeholder
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="Placeholder"/> class.
            /// </summary>
            /// <param name="title">The title of a placeholder</param>
            /// <param name="largeImageSource">The large <see cref="ImageSource"/> of a placeholder</param>
            protected Placeholder(string title, ImageSource largeImageSource)
            {
                this.Title = title;
                this.LargeImageSource = largeImageSource;
            }

            /// <summary>
            /// Gets a title of the placeholder
            /// </summary>
            public string Title { get; private set; }

            /// <summary>
            /// Gets a <see cref="ImageSource"/> of a placeholder
            /// </summary>
            public ImageSource LargeImageSource { get; private set; }
        }

        /// <summary>
        /// Placeholder of all <see cref="IModule"/> loaded into DSLPed application
        /// </summary>
        public class ModulePlaceholder : Placeholder
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="ModulePlaceholder"/> class.
            /// </summary>
            /// <param name="title">The title of a placeholder</param>
            /// <param name="largeImageSource">The large <see cref="ImageSource"/> of a placeholder</param>
            public ModulePlaceholder(string title, ImageSource largeImageSource, string moduleName)
                : base(title, largeImageSource)
            {
                this.ModuleName = moduleName;
            }

            /// <summary>
            /// Command being executed when we require to open a module
            /// </summary>
            public RelayCommand<string> OpenModuleCommand
            {
                get
                {
                    return
                        new RelayCommand<string>(
                            moduleName =>
                                ApplicationResources.Instance.Messenger.Send(new ViewModuleMessage(moduleName)));
                }
            }

            /// <summary>
            /// Name of the module to be opened
            /// </summary>
            public string ModuleName { get; protected set; }
        }

        /// <summary>
        /// Placeholder of all <see cref="IApplicationCommand"/> loaded into DSLPed application
        /// </summary>
        public class ApplicationCommandPlaceholder : Placeholder
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="ApplicationCommandPlaceholder"/> class.
            /// </summary>
            /// <param name="title">The title of a placeholder</param>
            /// <param name="largeImageSource">The large <see cref="ImageSource"/> of a placeholder</param>
            /// <param name="action">Action that is executed when the user clicks the corresponding action in RibbonBar</param>
            public ApplicationCommandPlaceholder(string title, ImageSource largeImageSource, Action action)
                : base(title, largeImageSource)
            {
                this.Command = (action == null) ? null : new RelayCommand(action);
            }

            /// <summary>
            /// Gets the <see cref="RelayCommand"/> that executes the action of this instance
            /// </summary>
            public RelayCommand Command { get; protected set; }
        }
        
        /// <summary>
        /// /// Placeholder of all generic commands loaded into DSLPed application
        /// </summary>
        /// <typeparam name="T">DataType of an object that is on the input of a corresponding action</typeparam>
        public class ApplicationCommandPlaceholder<T> : Placeholder
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="ApplicationCommandPlaceholder{T}"/> class.
            /// </summary>
            /// <param name="title">The title of a placeholder</param>
            /// <param name="largeImageSource">The large <see cref="ImageSource"/> of a placeholder</param>
            /// <param name="action">Action that is executed when the user clicks the corresponding action in RibbonBar</param>
            /// <param name="actionParameter">The parameter of the command</param>
            public ApplicationCommandPlaceholder(string title, ImageSource largeImageSource, Action<T> action, T actionParameter)
                : base(title, largeImageSource)
            {
                this.Command = (action == null) ? null : new RelayCommand<T>(action);
                this.CommandParameter = actionParameter;
            }

            /// <summary>
            /// Gets or sets the command of this placeholder
            /// </summary>
            public RelayCommand<T> Command { get; protected set; }

            /// <summary>
            /// Gets or sets the command parameter of this placeholder
            /// </summary>
            public T CommandParameter { get; protected set; }
        }

        #endregion SubClasses

        #region Members

        /// <summary>
        /// Gets all modules available to be opened withing DSLPed applicatoin
        /// </summary>
        public IEnumerable<ModulePlaceholder> Modules
        {
            get
            {
                var placeholders = new List<ModulePlaceholder>();
                foreach (var module in ApplicationResources.Instance.RegisteredModules)
                {
                    var largeImageSource = this.GetApplicationImageSource(module.ImageUri);

                    placeholders.Add(new ModulePlaceholder(module.Title, largeImageSource, module.Name));
                }

                return placeholders;
            }
        }

        /// <summary>
        /// Gets all application commands available to be exectued from the RibbonBar
        /// </summary>
        public IEnumerable<ApplicationCommandPlaceholder> ApplicationCommands
        {
            get
            {
                var placeholders = new List<ApplicationCommandPlaceholder>();
                foreach (var applicationCommand in ApplicationResources.Instance.RegisteredApplicationCommand)
                {
                    var largeImageSource = this.GetApplicationImageSource(applicationCommand.ImageUri);

                    placeholders.Add(new ApplicationCommandPlaceholder(applicationCommand.Title, largeImageSource, applicationCommand.Action));
                }

                return placeholders;
            }
        }

        /// <summary>
        /// Gets all available addIns whose corresponding file instances may be created
        /// after clicking a certain Ribbon button
        /// </summary>
        public IEnumerable<ApplicationCommandPlaceholder<Lazy<IDslPedAddIn, IDslPedAddInMetadata>>> AddIns
        {
            get
            {
                var placeholders = new List<ApplicationCommandPlaceholder<Lazy<IDslPedAddIn, IDslPedAddInMetadata>>>();
                foreach (var addIn in ApplicationResources.Instance.AvalableAddIns)
                {
                    var largeImageSource = AddInMetadataHelper.GetAddInImageSource(addIn.Metadata);

                    placeholders.Add(new ApplicationCommandPlaceholder<Lazy<IDslPedAddIn, IDslPedAddInMetadata>>(
                        addIn.Metadata.Extension, 
                        largeImageSource,
                        addInExt => ApplicationResources.Instance.Messenger.Send(new NewFileMessage(addInExt.Metadata.ProjectiveType, addInExt.Value.GetDefaultObject())),
                        addIn));
                }

                return placeholders;
            }
        }

        /// <summary>
        /// Gets the command that appears after the user clicks About
        /// </summary>
        public RelayCommand AboutCommand
        {
            get
            {
                return aboutCommand ?? (aboutCommand = new RelayCommand(OnAboutCommand));
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Gets <see cref="ImageSource"/> on a given path
        /// </summary>
        /// <param name="imageRelativePath">Relative path to the resource</param>
        /// <returns><see cref="ImageSource"/> created out of the resource</returns>
        private ImageSource GetApplicationImageSource(string imageRelativePath)
        {
            var largeImage = UriHelper.GetApplicationResource("DSLPedDesktop", imageRelativePath);
            try
            {
                var largeImageSource = new BitmapImage(largeImage);
                return largeImageSource;
            }
            catch
            {
                return ImageSourceHelper.GetDefaultImageSource("Resources/Images/puzzle.png", "DSLPedDesktop");
            }
        }

        /// <summary>
        /// Handler method of <see cref="AboutCommand"/>. 
        /// Opens an <see cref="AboutWindow"/> of DSLPed application
        /// </summary>
        private void OnAboutCommand()
        {
            var aboutWindow = new AboutWindow();
            aboutWindow.ShowDialog();
        }

        #endregion
    }
}
